package univers;

import fj.data.Natural;
import static fj.data.Natural.*;
import static univers.EspaceAffine.Z.*;

public class EspaceAffine {

    final static int minX = 1,  minY = 1,  maxX = 8,  maxY = 8;
    static Point Origine;
    static Point Frontiere;

    /**
     * Anneau Z construit sur Nat
     */
    public static class Z {

        static Z _0 = new Z(ZERO);
        static Z _1 = new Z(ONE);
        static Z s_1 = sym(ONE);
        public Integer I;
        static Z _8 = new Z(8);
        final static Z minX = _1,  minY = _1,  maxX = _8,  maxY = _8;

        Z(Integer I) {
            this.I = I;
        }

        Z(Natural n) {
            I = n.intValue();
        }

        static Z sym(Natural n) {
            return new Z(-1 * n.intValue());
        }

        Z plus(Z z) {
            return new Z(I + z.I);
        }

        Z times(Z z) {
            return new Z(I * z.I);
        }

        @Override
        public boolean equals(Object obj) {
            Z z = (Z) obj;
            return I.equals(z.I);
        }

        @Override
        public String toString() {
            return I.toString();
        }

        boolean inf(Z z) {
            return I < z.I;
        }

        boolean sup(Z z) {
            return I > z.I;
        }
    }

    /**
     * Module sur Z
     * 
     */
    EspaceAffine() {
        new Vecteur();
        Origine = new Point(_1, _1);
        Frontiere = new Point();

    }

    public static class Point implements Cloneable {

        Z X, Y;
        boolean Ext;
        private Integer I;
        private Integer J;

        public Point() {
            Ext = true;
        }

        Point(Z X, Z Y) {
            this.X = X;
            this.Y = Y;
        }

        public Point(Integer i, Integer j) {
           I=i;
           J=j;
        }

        public Vecteur toVecteur() {
            return new Vecteur(X, Y);
        }

        public boolean isExt() {
            return Ext;
        }

        @Override
        public String toString() {
            return "(" + X + "," + Y + ")";
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return new Point(this.X, this.Y);
        }

        @Override
        public boolean equals(Object objet) {
            if (objet == this) {
                return true;
            }
            if (objet instanceof Point) {
                Point p = (Point) objet;
                if ((X != p.X) | (Y != p.Y)) {
                    return false;
                } else {
                    return true;
                }
            }

            return false;
        }

        @Override
        public int hashCode() {
            int hash = 7;
            hash = 67 * hash + (this.X != null ? this.X.hashCode() : 0);
            hash = 67 * hash + (this.Y != null ? this.Y.hashCode() : 0);
            return hash;
        }

        public Z getX() {
            return X;
        }

        public Z getY() {
            return Y;
        }
    }

    public static class Vecteur implements Cloneable {

        Z u, v;
        public static Vecteur nord,  sud,  est,  ouest;
        public static Vecteur nord_est,  sud_est,  nord_ouest,  sud_ouest;
        public static Vecteur[] Direction;

        Vecteur(Z u, Z v) {
            this.u = u;
            this.v = v;
        }

        Vecteur() {
            Repere();
        }

        Point toPoint() {
            return new Point(u, v);
        }

        @Override
        public boolean equals(Object obj) {
            Vecteur V = (Vecteur) obj;
            return ((u.equals(V.u)) && (v.equals(V.v)));

        }

        @Override
        public int hashCode() {
            int hash = 5;
            hash = 43 * hash + (this.u != null ? this.u.hashCode() : 0);
            hash = 43 * hash + (this.v != null ? this.v.hashCode() : 0);
            return hash;
        }

        private void Repere() {
            nord = new Vecteur(_1, _0);
            est = new Vecteur(_0, _1);

            sud = sym(nord);
            ouest = sym(est);

            nord_est = nord.plus(est);
            sud_ouest = sym(nord_est);
            nord_ouest = nord.plus(ouest);
            sud_est = sym(nord_ouest);
            Direction = new Vecteur[8];
            Direction[0] = nord;
            Direction[1] = nord_est;
            Direction[2] = est;
            Direction[3] = sud_est;
            Direction[4] = sud;
            Direction[5] = sud_ouest;
            Direction[6] = ouest;
            Direction[7] = nord_ouest;
        }

        Vecteur sym(Vecteur vect) {
            return vect.times(s_1);
        }

        Vecteur plus(Vecteur vect) {
            return new Vecteur(u.plus(vect.u), v.plus(vect.v));
        }

        Vecteur times(Z z) {
            return new Vecteur(u.times(z), v.times(z));
        }

        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }

        @Override
        public String toString() {
            return "(" + u.toString() + "," + v.toString() + ")";
        }
    }
}
